#pragma once

/* lf segfit层，使用lazy页释放 
	大小阶(到哪为止)
	需要配置是否使用巨页(释放时按巨页释放)
*/
#include <metalc/mlayer/_mlayer.h>

#include <dbg.h>

#include <dinfra/mplf/tstack.h>
#include <metalc/block/cobj.h>

OU2INFUN(cls, seglf_cls)
OU2INFUN(cobj, seglf_cobj)
/* 无锁数据结构 */
typedef tstack_t seglf_list_t;

typedef struct seglf_obj
{
	size_t total_size;
	size_t pad;
	seglf_list_t flists[];
} seglf_obj_t;


/* MADV建议free */
typedef void (mark_page_func_t)(void *ptr, size_t sz);
typedef const struct seglf_conf {
	const szcls_t total_szcls;
	const size_t min_size;
	const size_t page_size;
	mark_page_func_t * const mark_used;
	mark_page_func_t * const mark_unused;
} seglf_conf_t;

SAI size_t seglf_list_size(const szcls_t total_szcls){
	return sizeof(tstack_t) * total_szcls;
}
SAI size_t seglf_objsize(const szcls_t total_szcls){
	/* cache对齐 */
	return align_up(sizeof(seglf_obj_t), HDEF_CACHE_LINE_BYTES) + seglf_list_size(total_szcls);
}
SAI NO_UNROLL seglf_list_t *seglf_list_init(void *mem, size_t size, const szcls_t total_szcls){
	size_t total_sz = seglf_list_size(total_szcls);
	ASSERT(mem && (size >= total_sz));
	seglf_list_t *m = (seglf_list_t *)mem;
	for (szcls_t idx = 0; idx < total_szcls; idx++){
		tstack_init(&m[idx]);
	}
	return m;
}

SAI bool_t seglf_list_push_batch(seglf_list_t *m, tsnode_t *head, tsnode_t *tail, szcls_t cls, size_t cnt) {
	PARAM_USED(cnt);
	return __tstack_push_batch(&m[cls], head, tail);
}

SAI tsnode_t *seglf_list_pop(seglf_list_t *m, szcls_t cls){
	tsnode_t *nd;
	__tstack_pop(&m[cls], &nd);
	return nd;
}

SIN seglf_obj_t *seglf_obj_init(void *mem, size_t size, const szcls_t total_szcls){
	size_t total_sz = seglf_objsize(total_szcls);
	ASSERT(mem && (size >= total_sz));
	seglf_obj_t *s = (seglf_obj_t *)mem;
	s->total_size = 0;
	seglf_list_init(&s->flists[0], size - sizeof(seglf_obj_t), total_szcls);
	return s;
}

SAI void *seglf_oomhandler(mreq_t *req, size_t size, const size_t min_size, const mlfunc_t * cont) {
	// 确保nsize已经被设置
	req->size = size;
	req->align = min_size;
	req->nr = 1;
	uvoid *ret = cont->lalloc(req);
	INFO("[segfit] oom get %p, req size 0x%lx\n", ret, req->size);
	return ret;
}

SAI void __seglf_commit(void *mem, size_t size, mark_page_func_t *mark_page, const size_t page_size) {
	#ifdef SEGLF_DECOMMIT_MINSIZE
	ASSERT(mem);
	PARAM_USED(page_size);
	if (size >= SEGLF_DECOMMIT_MINSIZE){
		// mark_page((void *)((paddr_t)mem), size);
		mark_page((void *)((paddr_t)mem + page_size), size - page_size);
	}
		
	#else
	PARAM_USED(mem, size, mark_page, page_size);
	#endif
}
SAI void seglf_commit(void *mem, size_t size, mark_page_func_t *mark_page_used, const size_t page_size){
	__seglf_commit(mem, size, mark_page_used, page_size);
}
SAI void seglf_decommit(void *mem, size_t size, mark_page_func_t *mark_page_unused, const size_t page_size){
	__seglf_commit(mem, size, mark_page_unused, page_size);
}

SAI void _seglf_pinit(const mlfunc_t * cont, seglf_obj_t **pobj, perm_allocf_t * const alloc, const szcls_t total_szcls){
	PARAM_USED(cont);
	INFO("_seglf pinit\n");
	if (!*pobj){
		void *mem = alloc(seglf_objsize(total_szcls));
		ASSERT(mem && is_ptr_aligned(mem, sizeof(dword_t)));
		*pobj = seglf_obj_init(mem, seglf_objsize(total_szcls), total_szcls);	
	}
}
SAI void _seglf_tinit(const mlfunc_t * cont){
	PARAM_USED(cont);
	INFO("_seglf tinit\n");
}
SAI void *_seglf_alloc(mreq_t *req,  const mlfunc_t * cont, seglf_obj_t *l, seglf_conf_t *conf, INFUN(seglf_cls, clspol), INFUN(seglf_cobj, cobj)){
	
	void *ret = NULL_PTR;
	if (UNLIKELY(MLAYER_INVALID_REQSZ(cont, req->size))) { 
		return cont->lalloc(req); 
	}
	
	size_t size = align_up(req->size, conf->min_size);
	szcls_t cls = clspol->sz2cls(size);
	size_t aligned_size = clspol->cls2sz(cls);
	INFO("[seglf] try alloc sz %lx, cls %u\n", aligned_size, cls);
	/* 尝试从无锁队列中出队 */
	// if (!tstack_is_empty(&l->flists[0]))
		ret = seglf_list_pop(&l->flists[0], cls);
	
	void *mem = cobj->uptr2ptr(ret);
	
	/* 不需要再commit */
	// seglf_commit(mem, aligned_size, conf->mark_used, conf->page_size);

	if (UNLIKELY(!ret)) {
		mem = seglf_oomhandler(req, aligned_size, conf->min_size, cont);
		if (UNLIKELY(!mem)) return NULL_PTR;
		ret = cobj->ptr2uptr(mem);
		cobj->set_cobj(mem, aligned_size, 1);
	} else {
		/* TODO: 这个分支 其实不需要吧? */
		// ASSERT(cobj->ptr_getsize(mem) == aligned_size);
		/* 设置meta信息为used, 如果之前设置了则避免设置防止产生额外的cache false sharing */
		/* if (!(cobj->ptr_getsize(mem) == aligned_size))
			cobj->set_cobj(mem, aligned_size, 1); */
	}
	/* 调试使用 */
	PARAM_USED(mem);
	INFO("[seglf] alloc %p, return %p, sz %lx, cls %u\n", mem, ret, aligned_size, cls);


	// /* FIXME: 注意，分配地址的头部两个字(sizeof(dword_t))的大小不能分配给用户使用吗？否则可能出现ABA问题 */
	req->ptr = ret;
	return ret;
}
SAI bool_t _seglf_free(mreq_t *req,  const mlfunc_t * cont, seglf_obj_t *l, seglf_conf_t *conf, INFUN(seglf_cls, clspol), INFUN(seglf_cobj, cobj)){
	
	if (MLAYER_INVALID_REQSZ(cont, req->size)) { 
		return cont->lfree(req); 
	}

	size_t size = req->size;
	szcls_t cls = clspol->sz2cls(size);

	uvoid *uhead = cobj->ptr2uptr(req->ptr);
	uvoid *utail = cobj->ptr2uptr(req->sptr);
	if (req->nr == 1){
		utail = uhead;
	}

	/* 设置meta信息为free(其实没必要) */
	// cobj->set_cobj(req->ptr, size, 0);
	/* 先decommit */
	seglf_decommit(req->ptr, size, conf->mark_unused, conf->page_size);
	/* 插入无锁队列 */
	seglf_list_push_batch(&l->flists[0], (tsnode_t *)uhead, (tsnode_t *)utail, cls, req->nr);
	INFO("[seglf] free %p - %p, sz %lx, cls %u\n", uhead, utail, size, cls);
	return TRUE;
}

SAI size_t _seglf_sizeof(mreq_t *req, const mlfunc_t * cont, INFUN(seglf_cls, clspol), INFUN(seglf_cobj, cobj)){
	if (MLAYER_IS_LASTLAYER(cont) || is_ptr_aligned(req->ptr, cont->lmaxsize)){
		PARAM_USED(clspol);
		void *ptr = cobj->uptr2ptr(req->ptr);
		size_t size = cobj->ptr_getsize(ptr);
		req->ptr = ptr;
		req->size = size;
		INFO("[seglf] sizeof %p, sz %lx\n", req->ptr, size);
		return size - cobj->mocupysz();
	}
	return cont->lsizeof(req);
}

#include <metalc/block/cobj.h>
/* 定义 
 * min_bit - 管理的最小bit位
 * max_bit - 管理的最大bit位
 * cls_comp - szcls策略
 * perm_alloc - 永久对象分配函数
*/
#define DEF_SEGLF_2B_MLAYER(name, min_bit, max_bit, PAGE_SIZEZ, mark_used_func, mark_unused_func, perm_alloc, oalloc)\
DEF_MLAYER_BASIC(name, BIT(max_bit), 0)\
DEF_CLS_POW2B(name##_2b, min_bit, HDEF_PTR_BITS)\
static cobj_ord_t *pext_##name;\
COBJ_EXT(ext_##name, pext_##name, BIT(min_bit))\
static seglf_obj_t *name##_obj;\
static const seglf_conf_t name##_conf = { .total_szcls = _clsnr_##name##_2b, .min_size = BIT(min_bit),\
.page_size = PAGE_SIZEZ, .mark_used = mark_used_func, .mark_unused_func = mark_unused_func };\
SAI void name##_pinit(const mlfunc_t * cont){ if(!pext_##name){ \
	pext_##name = oalloc(BIT(HDEF_PTR_BITS)/BIT(min_bit));\
	}\
	return _seglf_pinit(cont, &name##_obj, perm_alloc, _clsnr_##name##_2b); }\
SAI void name##_tinit(const mlfunc_t * cont){ return _seglf_tinit(cont); }\
SAI void *name##_alloc(mreq_t *req, const mlfunc_t * cont){\
	return _seglf_alloc(req, cont, name##_obj, &name##_conf, OUFUN_OF(name##_2b), OUFUN_OF(ext_##name));\
}\
SAI bool_t name##_free(mreq_t *req, const mlfunc_t * cont){\
	return _seglf_free(req, cont, name##_obj, &name##_conf, OUFUN_OF(name##_2b), OUFUN_OF(ext_##name));\
}\
SAI size_t name##_sizeof(mreq_t *req, const mlfunc_t * cont){\
	return _seglf_sizeof(req, cont, OUFUN_OF(name##_2b), OUFUN_OF(ext_##name)); }
